using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace Surveillance
{

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Player player;
        List<Wall> wallList;
        List<Enemy> enemyList;
        List<Switch> switchList;
        List<PressurePlate> plateList;
        List<Door> doorList;
        List<Elevator> elevatorList;
        List<Alarm> alarmList;
        List<Keypad> padList;
        Robot robot;
        Goal goal;
        bool playing;
        bool won;
        public static SpriteFont font;
        public static Camera2d cam;
        public static bool activeTextBox = false;
        public static readonly float GRAVITY = 0.5f;
        public static readonly float[] ZOOM_LEVEL = {0.5f, 3.0f};
        public static int currentZoomLevel = 0;
        public static  float WORLD_HEIGHT = 3000;
        public static  float WORLD_WIDTH = 3000;

        
        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
            playing = true;
            won = false;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 800;
            graphics.PreferredBackBufferHeight = 600;
            graphics.PreferredDepthStencilFormat = DepthFormat.Depth24;
            graphics.GraphicsDevice.DepthStencilState = DepthStencilState.Default;
            graphics.ApplyChanges();
            Mouse.WindowHandle = Window.Handle;
            Mouse.SetPosition((int)graphics.GraphicsDevice.Viewport.Width / 2, (int)graphics.GraphicsDevice.Viewport.Height / 2);
            IsMouseVisible = true;
            cam = new Camera2d(graphics.GraphicsDevice);
            cam.Zoom = ZOOM_LEVEL[0];
            base.Initialize();    
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);
            font = Content.Load<SpriteFont>(@"Textures\SpriteFont1");
            player = new Player(Content, graphics.GraphicsDevice, Window);
            robot = new Robot(Content, new Vector2(1000.0f, 1370.0f), 30, 30, graphics.GraphicsDevice);
            goal = new Goal(Content.Load<Texture2D>(@"Textures\goal"), new Rectangle(2000,1370,60,60));
            load_walls();
            load_switches();
            load_enemies();
        }

        private void load_walls()
        {
            wallList = new List<Wall>();
            //top level
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width, 250), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width * 2 - 200, 10));

            //level 2
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width + 200, 550), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width * 2 - 200, 10));

            //level 3
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width, 850), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width * 2 - 200, 10));

            //level 4
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width + 200, 1150), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width * 2 - 200, 10));

            //level 5
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width, 1450), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width * 2, 10));

            //boundries
            //top wall
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width, 0), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width*2, 10));
            //bottom wall
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width, WORLD_HEIGHT - graphics.GraphicsDevice.Viewport.Height), (int)WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width*2, 10));
            //left wall
            wallList.Add(new Wall(Content, new Vector2(graphics.GraphicsDevice.Viewport.Width, 0), 10, (int)WORLD_HEIGHT - graphics.GraphicsDevice.Viewport.Height));
            //right wall
            wallList.Add(new Wall(Content, new Vector2(WORLD_WIDTH - graphics.GraphicsDevice.Viewport.Width, 0), 10, (int)WORLD_HEIGHT - graphics.GraphicsDevice.Viewport.Height));
        }

        private void load_enemies()
        {
            //initialization
            padList = new List<Keypad>();
            alarmList = new List<Alarm>();
            enemyList = new List<Enemy>();
            Alarm.alarmTexture = Content.Load<Texture2D>(@"Textures\alarm");
            Keypad.padTexture = Content.Load<Texture2D>(@"Textures\keypad");
            Keypad.boxTexture = Content.Load<Texture2D>(@"Textures\textBox");

            //level 4
            List<Vector2> lvl4_enemy_waypoints = new List<Vector2>();
            lvl4_enemy_waypoints.Add(new Vector2(2100,0));
            lvl4_enemy_waypoints.Add(new Vector2(1100, 0));
            Enemy lvl4_enemy = new Enemy(Content, new Vector2(1100, 1050), 30, 30, graphics.GraphicsDevice, lvl4_enemy_waypoints); 
            enemyList.Add(lvl4_enemy);

            //level 5
            List<Vector2> lvl5_enemy_waypoints = new List<Vector2>();
            lvl5_enemy_waypoints.Add(new Vector2(1100, 0));
            List<Vector2> lvl5_alarm_waypoints = new List<Vector2>();
            lvl5_alarm_waypoints.Add(new Vector2(2100, 0));
            Enemy lvl5_enemy = new Enemy(Content, new Vector2(1100,1200), 30, 30, graphics.GraphicsDevice, lvl5_enemy_waypoints);
            Alarm lvl5_alarm = new Alarm(new Rectangle(2100, 1300, 60, 60), lvl5_enemy, lvl5_alarm_waypoints);
            Switch lvl5_alarm_switch = new Switch(new Vector2(950, 1350), 40, 40, 20 , 600);
            Door lvl5_door1 = new Door(new Rectangle(1750,1160,10,290));
            Keypad lvl5_keypad = new Keypad(new Rectangle(1600, 1300, 40, 40), "the cake is a lie",lvl5_enemy, lvl5_door1);
            PressurePlate lvl5_door1_plate = new PressurePlate(new Vector2(1800, 1450), 250, 10, 1);
            lvl5_alarm_switch.addLink(lvl5_alarm,0);
            lvl5_door1_plate.addLink(lvl5_door1);
            enemyList.Add(lvl5_enemy);
            alarmList.Add(lvl5_alarm);
            switchList.Add(lvl5_alarm_switch);
            doorList.Add(lvl5_door1);
            padList.Add(lvl5_keypad);
            plateList.Add(lvl5_door1_plate);

        }

        private void load_switches()
        {
            //initialization
            switchList = new List<Switch>();
            plateList = new List<PressurePlate>();
            doorList = new List<Door>();
            elevatorList = new List<Elevator>();
            Switch.switchTexture = Content.Load<Texture2D>(@"Textures\switch");
            Switch.font = Content.Load<SpriteFont>(@"Textures\SpriteFont1");
            PressurePlate.plateTexture = Content.Load<Texture2D>(@"Textures\plate");
            Door.doorTexture = Content.Load<Texture2D>(@"Textures\door");
            Elevator.elevatorTexture = Content.Load<Texture2D>(@"Textures\elevator"); 

            //level 2 
            Switch lvl2_door_switch = new Switch(new Vector2(890, 450), 40, 40, 110, -70);
            Door lvl2_door = new Door(new Rectangle(1100, 260, 10, 290));
            lvl2_door_switch.addLink(lvl2_door, 0);
            switchList.Add(lvl2_door_switch);
            doorList.Add(lvl2_door);

            //level 3
            Switch lvl3_door1_switch = new Switch(new Vector2(810, 750), 40, 40, 15, 140);
            PressurePlate lvl3_door1_plate = new PressurePlate(new Vector2(1700, 850), 200, 10,1);
            Door lvl3_door1 = new Door(new Rectangle(1990, 560, 10, 290));
            Switch lvl3_door2_switch = new Switch(new Vector2(2100, 750), 40, 40, 1300, 140);
            Door lvl3_door2 = new Door(new Rectangle(1900, 560, 10, 290));
            lvl3_door1_switch.addLink(lvl3_door1,0);
            lvl3_door1_plate.addLink(lvl3_door1);
            lvl3_door2_switch.addLink(lvl3_door2,0);
            switchList.Add(lvl3_door1_switch);
            switchList.Add(lvl3_door2_switch);
            plateList.Add(lvl3_door1_plate);
            doorList.Add(lvl3_door1);
            doorList.Add(lvl3_door2);

            //level 4
            Switch lvl4_door1_door2_switch = new Switch(new Vector2(2100, 1050), 40, 40, 1300, 470);
            Door lvl4_door1 = new Door(new Rectangle(1700, 860, 10, 290));
            Switch lvl4_door2_elevator1_switch = new Switch(new Vector2(1600, 1050), 40, 40, 810, 440);
            Door lvl4_door2 = new Door(new Rectangle(1000, 860, 10, 290));
            Elevator lvl4_elevator1 = new Elevator(new Rectangle(1800, 1150, 200, 10), 920, 1150);
            lvl4_door1_door2_switch.addLink(lvl4_door1, 0);
            lvl4_door1_door2_switch.addLink(lvl4_door2, 0);
            lvl4_door2_elevator1_switch.addLink(lvl4_door2, 0);
            lvl4_door2_elevator1_switch.addLink(lvl4_elevator1,-1);
            switchList.Add(lvl4_door1_door2_switch);
            switchList.Add(lvl4_door2_elevator1_switch);
            doorList.Add(lvl4_door1);
            doorList.Add(lvl4_door2);
            elevatorList.Add(lvl4_elevator1);
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            if (playing)
            {
                // Allows the game to exit
                if (Keyboard.GetState().IsKeyDown(Keys.Escape) && !activeTextBox)
                    this.Exit();
                if (robot.TargetRectangle.Intersects(goal.TargetLocation))
                {
                    playing = false;
                    won = true;
                }
                player.Update(gameTime, switchList, padList);
                robot.Update(gameTime, wallList, doorList, plateList, elevatorList);
                update_enemies(gameTime);
                update_elevators(gameTime);
                update_alarms(gameTime);
                update_keypads(gameTime);
                checkIfCaught();
                base.Update(gameTime);
            }
            else
            {
                if (Keyboard.GetState().GetPressedKeys().Count() > 0)
                {
                    reset_game();
                    won = false;
                    playing = true;
                }
            }
        }

        private void update_enemies(GameTime gameTime)
        {
            foreach (Enemy enemy in enemyList)
            {
                enemy.Update(gameTime, wallList, doorList, elevatorList, plateList);
            }
        }

        private void update_elevators(GameTime gameTime)
        {
            foreach (Elevator elevator in elevatorList)
            {
                elevator.Update(gameTime);
            }
        }

        private void update_alarms(GameTime gameTime)
        {
            foreach (Alarm alarm in alarmList)
            {
                alarm.Upadte(gameTime);
            }
        }

        private void update_keypads(GameTime gameTime)
        {
            foreach (Keypad pad in padList)
            {
                pad.Update(gameTime);
            }
        }
        
        private void checkIfCaught()
        {
            foreach (Enemy enemy in enemyList)
            {
                if (enemy.TargetRectangle.Intersects(robot.TargetRectangle))
                {
                    playing = false;
                }
            }
        }

        private void reset_game()
        {
            robot.reset();
            reset_enemy();
            reset_doors();
            reset_switches();
            reset_elevators();
            reset_alarms();
            reset_keypads();
        }

        private void reset_enemy()
        {
            foreach (Enemy enemy in enemyList)
            {
                enemy.reset();
            }
        }

        private void reset_elevators()
        {
            foreach (Elevator elevator in elevatorList)
            {
                elevator.reset();
            }
        }

        private void reset_switches()
        {
            foreach (Switch swtch in switchList)
            {
                swtch.reset();
            }
        }

        private void reset_doors()
        {
            foreach (Door door in doorList)
            {
                door.reset();
            }
        }

        private void reset_alarms()
        {
            foreach (Alarm alarm in alarmList)
            {
                alarm.reset();
            }
        }

        private void reset_keypads()
        {
            foreach (Keypad pad in padList)
            {
                pad.reset();
            }
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.White);

            Matrix mat = cam.get_transformation(graphics.GraphicsDevice);
            if (playing)
            {
                spriteBatch.Begin(SpriteSortMode.BackToFront,
                            BlendState.AlphaBlend,
                            null,
                            null,
                            null,
                            null,
                            mat);
                player.Draw(spriteBatch);
                robot.Draw(spriteBatch);
                goal.Draw(spriteBatch);
                draw_elevators();
                draw_walls();
                draw_enemies();
                draw_switches();
                draw_plates();
                draw_doors();
                draw_alarms();
                draw_keypads();
                spriteBatch.End();
                spriteBatch.Begin();
                draw_textBox();
                spriteBatch.End();
                base.Draw(gameTime);
            }
            else
            {
                spriteBatch.Begin();
                string msg;
                string lost_msg = "You got caught!\n press any key to restart...";
                string won_msg = "you won!\n press any key to restart...";
                if (won) msg = won_msg; else msg = lost_msg;
                spriteBatch.DrawString
                    (font,
                    msg,
                    new Vector2(graphics.GraphicsDevice.Viewport.Width / 2.5f, graphics.GraphicsDevice.Viewport.Height / 2),
                    Color.Black);
                spriteBatch.End();
            }
        }

        private void draw_walls()
        {
            foreach (Wall wall in wallList)
            {
                wall.Draw(spriteBatch);
            }
        }

        private void draw_enemies()
        {
            foreach (Enemy enemy in enemyList)
            {
                enemy.Draw(spriteBatch);
            }
        }

        private void draw_switches()
        {
            foreach (Switch swtch in switchList)
            {
                swtch.Draw(spriteBatch);
            }
        }

        private void draw_doors()
        {
            foreach (Door door in doorList)
            {
                door.Draw(spriteBatch);
            }
        }

        private void draw_plates()
        {
            foreach(PressurePlate plate in plateList)
            {
                plate.Draw(spriteBatch);
            }
        }

        private void draw_elevators()
        {
            foreach (Elevator elevator in elevatorList)
            {
                elevator.Draw(spriteBatch);
            }
        }

        private void draw_alarms()
        {
            foreach (Alarm alarm in alarmList)
            {
                alarm.Draw(spriteBatch);
            }
        }

        private void draw_keypads()
        {
            foreach (Keypad pad in padList)
            {
                pad.Draw(spriteBatch);
            }
        }

        private void draw_textBox()
        {
            foreach (Keypad pad in padList)
            {
                if (pad.Active)
                {
                    pad.DrawTextBox(spriteBatch);
                    break;
                }
            }
        }

    }
}
